﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestTools.WebTesting;
using System.ComponentModel;
using System.IO;
using System.Diagnostics;
using System.Xml.Serialization;
using Dropthings.Test.Rules;
using Inventory.Test.Library.Plugin;
using Inventory.Test.Library.Rules;

namespace Inventory.TestRecorderExtensions
{

    // [AlwaysExecuteRecorderPlugin]
    public class ControlIDShrinker : WebTestRecorderPlugin
    {
        string[] noisyParameters = { 
                                       "_PopulateLog",
                                       "_SelectedNode",
                                       "_ExpandState",
                                       "__SCROLLPOSITIONX",
                                       "__SCROLLPOSITIONY",
                                       "__LASTFOCUS",
                                       "_ClientState"
                                   };

        string[] parametersHasNoisyValues = {
                                                "__EVENTTARGET",
                                                "__EVENTARGUMENT"
                                            };



        public override void PostWebTestRecording(object sender, PostWebTestRecordingEventArgs e)
        {

        }
        public override void PostWebTestDynamicParameterDetection(object sender, PostWebTestRecordingEventArgs e)
        {
            System.Diagnostics.Debugger.Launch();

            var logger = File.CreateText(@"C:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE\PrivateAssemblies\WebTestPlugins\log.txt");

            try
            {
                logger.WriteLine("Current folder: " + Environment.CurrentDirectory);
                logger.WriteLine("Current Process Name: " + Process.GetCurrentProcess().ProcessName);
                logger.WriteLine("Current Process ID: " + Process.GetCurrentProcess().Id);
                logger.WriteLine("Current Process Started At: " + Process.GetCurrentProcess().StartTime);


                logger.WriteLine("Recorded Web Test Result has " + e.RecordedWebTestResult.Children.Count + " Children");
                logger.WriteLine("Recorded Web Test  has " + e.RecordedWebTest.Items.Count + " Children");

                ClearNoiseFromRequests(logger, e);

                for (int counter = 0; counter < e.RecordedWebTestResult.Children.Count; counter++)
                {
                    var resultUnit = e.RecordedWebTestResult.Children[counter];

                    if (resultUnit == null)
                    {
                        continue;
                    }

                    logger.Write("Start dealing with Children in index " + counter);


                    WebTestResultPage pageResult = resultUnit as WebTestResultPage;
                    var webTestRequest = e.RecordedWebTest.GetItem(pageResult.DeclarativeWebTestItemId) as WebTestRequest;

                    if (webTestRequest == null)
                    {
                        logger.WriteLine("\twebTestRequest with DeclarativeWebTestItemId = " + pageResult.DeclarativeWebTestItemId + " is null ");
                        continue;
                    }
                    if (string.IsNullOrEmpty(pageResult.RequestResult.Response.ContentType))
                    {
                        logger.WriteLine("\tRequestResult.Response.ContentType == null");
                        continue;
                    }

                    ExtractElements(logger, pageResult.RequestResult.Response);

                    var body = webTestRequest.Body as FormPostHttpBody;
                    if (body != null)
                    {
                        ClearNoiseFromFormPostParametersNames(logger, body);
                        ClearNoiseFromFormPostParametersValues(logger, body.FormPostParameters);
                    }
                    ClearSelectorsDictionary(logger, e, counter, webTestRequest);

                    e.RecordedWebTestModified = true;
                }


                if (e.RecordedWebTest.WebTestPluginReferences.Where(d => d.DisplayName == "ASPNETWebTestPlugin").Count() == 0)
                {
                    e.RecordedWebTest.WebTestPluginReferences.Add(new WebTestPluginReference()
                    {
                        DisplayName = "ASPNETWebTestPlugin",
                        Type = Type.GetType("Dropthings.Test.Plugins.ASPNETWebTestPlugin, Inventory.TestExtensions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")
                    });
                }
                if (e.RecordedWebTest.WebTestPluginReferences.Where(d => d.DisplayName == "Generate Date").Count() == 0)
                {
                    e.RecordedWebTest.WebTestPluginReferences.Add(new WebTestPluginReference()
                    {
                        DisplayName = "Generate Date",
                        Type = typeof(GenerateDatesPlugin)
                    });
                }
                if (e.RecordedWebTest.DataSources.Where(d => d.Name == "Config").Count() == 0)
                {
                    e.RecordedWebTest.AddDataSource("Config",
                                "Microsoft.VisualStudio.TestTools.DataSource.XML",
                                "|DataDirectory|\\..\\..\\TestParameters.xml",
                                DataBindingAccessMethod.Sequential, "TestParameters");
                }

                e.RecordedWebTestModified = true;

            }
            catch (Exception ex)
            {
                logger.WriteLine("EXCEPTION: " + ex.ToString());
                if (ex.InnerException != null)
                    logger.WriteLine("INNER EXCEPTION: " + ex.InnerException.ToString());

                throw;
            }
            finally
            {
                logger.Close();
            }

        }

        private void ExtractElements(StreamWriter logger, WebTestResponse webTestResponse)
        {
            ExtractUpdatePanelsAndGrids(logger, webTestResponse);
            var formElements = ExtractFormElements.Extract(webTestResponse.BodyString);
            formElements.AddRange(ExtractScriptManagers.Extract(webTestResponse.BodyString));

            foreach (FormElement element in formElements)
            {
                logger.WriteLine("\tShrinking form element " + element.ControlFullID);
                string shrinkedID = ShrinkID(logger, element.ControlFullID);
                if (shrinkedID == element.ControlFullID)
                    continue;

                if (!idDictionary.ContainsKey(element.ControlFullID))
                {
                    idDictionary.Add(element.ControlFullID, null);
                    logger.WriteLine(string.Format("\t\t\tidDictionary.Add({0}, null);", element.ControlFullID));
                }

                logger.WriteLine(string.Format("\t\t\tidDictionary[{0}] = {1}", element.ControlFullID, shrinkedID));
                idDictionary[element.ControlFullID] = shrinkedID;
            }
        }
        //private void AppendGridSelectors(StreamWriter logger, WebTestResultPage pageResult)
        //{
        //    List<string> gridIDList = ExtractGridViews.Extract(pageResult.RequestResult.Response.BodyString);
        //    foreach (string grid in gridIDList)
        //    {
        //        logger.WriteLine("\tShrinking grid " + grid);
        //        ShrinkID(logger, grid);

        //    }
        //}
        private void ExtractUpdatePanelsAndGrids(StreamWriter logger, WebTestResponse response)
        {
            logger.WriteLine("\tExtracting update panels & grids ");

            List<UpdatePanelExtracationData> updatePanels = new List<UpdatePanelExtracationData>();
            if (ExtractUpdatePanels.IsHtmlResponse(response))
            {
                logger.WriteLine("\tHtml Response detected");
                updatePanels = ExtractUpdatePanels.ExtractUpdatePanelNamesFromHtml(response.BodyString);
            }
            //else
            //{
            //    updatePanels = ExtractUpdatePanels.ExtractUpdatePanelNamesFromAsyncPostback(pageResult.RequestResult.Response.BodyString);
            //}
            foreach (UpdatePanelExtracationData up in updatePanels)
            {
                logger.WriteLine("\tShrinking update panel " + up.UpdatePanelName);
                string shrinkedID = ShrinkID(logger, up.UpdatePanelName);
                if (!idDictionary.ContainsKey(up.UpdatePanelName))
                {
                    idDictionary.Add(up.UpdatePanelName, null);
                    logger.WriteLine(string.Format("\t\t\tidDictionary.Add({0}, null);", up.UpdatePanelName));
                }

                logger.WriteLine(string.Format("\t\t\tidDictionary[{0}] = {1}", up.UpdatePanelName, shrinkedID));
                idDictionary[up.UpdatePanelName] = shrinkedID;
            }
        }

        private void ClearNoiseFromRequests(StreamWriter logger, PostWebTestRecordingEventArgs e)
        {
            for (int counter = 0; counter < e.RecordedWebTestResult.Children.Count; counter++)
            {
                var u = e.RecordedWebTestResult.Children[counter];
                if (u == null)
                {
                    continue;
                }

                WebTestResultPage pageResult = u as WebTestResultPage;
                var webTestRequest = e.RecordedWebTest.GetItem(pageResult.DeclarativeWebTestItemId) as WebTestRequest;
                if (webTestRequest == null)
                {
                    logger.WriteLine("webTestRequest == null");
                    continue;
                }
                if (webTestRequest.Url == null)
                {
                    logger.WriteLine("webTestRequest.Url == null");
                    continue;
                }
                if (webTestRequest.Url.Contains("ScriptResource.axd"))
                {
                    e.RecordedWebTestResult.Children.RemoveAt(counter);
                    e.RecordedWebTest.Items.RemoveAt(counter);
                    counter--;
                    logger.WriteLine("\tScriptResource.axd is noisy children & removed successfully, children count is now " + e.RecordedWebTestResult.Children.Count);
                }
                else
                {
                    logger.WriteLine(" and Url = " + webTestRequest.Url);
                    webTestRequest.Url = ClearNoiseFromUrl(webTestRequest.Url);
                    logger.WriteLine(" Url Now is = " + webTestRequest.Url);
                }
            }
        }


        private string ClearNoiseFromUrl(string uri)
        {
            string urlPrefix = "{{Config.TestParameters.ServerURL}}";
            int endAt = 0;
            if (uri.ToLower().Contains("localhost:"))
            {
                endAt = uri.IndexOf('/', uri.IndexOf("localhost"));
            }
            else
            {
                endAt = uri.IndexOf("//");
                endAt = uri.IndexOf('/', endAt + 2);
                endAt = uri.IndexOf('/', endAt + 1);
            }

            uri = uri.Remove(0, endAt);
            return urlPrefix + uri;

        }



        private void ClearSelectorsDictionary(StreamWriter logger, PostWebTestRecordingEventArgs e, int counter, WebTestRequest webTestRequest)
        {
            WebTestRequest nextRequest = null;
            int nextPageIndex = counter + 1;
            if (e.RecordedWebTestResult.Children.Count > nextPageIndex)
            {
                var page = e.RecordedWebTestResult.Children[nextPageIndex] as WebTestResultPage;
                nextRequest = e.RecordedWebTest.GetItem(page.DeclarativeWebTestItemId) as WebTestRequest;
            }


            var isInSamePage = false;
            logger.WriteLine("webTestRequest.Url = " + webTestRequest.Url);
            if (nextRequest != null)
            {
                isInSamePage = nextRequest.Url == webTestRequest.Url;
                logger.WriteLine("nextRequest.Url = " + nextRequest.Url);
                logger.WriteLine("IsInSamePage = " + isInSamePage);
            }
            var currentUrlWithQueryString = webTestRequest.UrlWithQueryString;
            logger.WriteLine("currentUrlWithQueryString = " + currentUrlWithQueryString);
            if (nextRequest != null)
            {
                var nextPageNameStartFrom = nextRequest.Url.LastIndexOf('/') + 1;
                var nextPageName = nextRequest.Url.Substring(nextPageNameStartFrom, nextRequest.Url.Length - nextPageNameStartFrom);
                logger.WriteLine("currentUrlWithQueryString = " + currentUrlWithQueryString + " Next page name = " + nextPageName);
                if (isInSamePage == false && !string.IsNullOrEmpty(currentUrlWithQueryString) && currentUrlWithQueryString.Contains("?ReturnUrl=") && currentUrlWithQueryString.Contains(nextPageName))
                {
                    isInSamePage = true;
                    logger.WriteLine("IsInSamePage = " + isInSamePage);
                }
            }

            logger.WriteLine("IsInSamePage = " + isInSamePage);

            if (nextRequest == null || isInSamePage == false)
            {
                logger.WriteLine("Clearing idDictionary");
                idDictionary.Clear();
            }
        }

        private void ClearNoiseFromFormPostParametersNames(StreamWriter logger, FormPostHttpBody body)
        {
            logger.WriteLine("\tBody of the pulled WebTestRequest is casted as FormPostHttpBody");
            if (body.FormPostParameters == null)
            {
                logger.WriteLine("\tbody.FormPostParameters is null");
                return;
            }
            logger.WriteLine("\tBody has " + body.FormPostParameters.Count + " Form post parameters");
            for (int i = 0; i < body.FormPostParameters.Count; i++)
            {
                var param = body.FormPostParameters[i];
                string fullName = param.Name;
                string parameterSuffix = "";

                if (fullName.EndsWith(".x", StringComparison.OrdinalIgnoreCase) || fullName.EndsWith(".y", StringComparison.OrdinalIgnoreCase))
                {
                    int startTrimmingFrom = fullName.LastIndexOf(".");
                    parameterSuffix = fullName.Substring(startTrimmingFrom, 2);
                    fullName = fullName.Substring(0, startTrimmingFrom);
                }
                logger.WriteLine("\t\tpost paramter at index " + i + " with name = " + fullName + " and value = " + param.Value);
                if (ShouldBeRemoved(fullName))
                {
                    body.FormPostParameters.RemoveAt(i);
                    i--;
                    logger.WriteLine("\t\tTaged as noisy parameter and removed successfully, parameters count is " + body.FormPostParameters.Count);
                    continue;
                }

                if (idDictionary.ContainsKey(param.Name))
                {
                    param.Name = "{{" + idDictionary[param.Name] + "}}" + parameterSuffix;
                }
            }
            return;
        }
        private void ClearNoiseFromFormPostParametersValues(StreamWriter logger, FormPostParameterCollection formPostParameterCollection)
        {
            logger.WriteLine("\tStart clearing noise from parameters values");

            foreach (var param in formPostParameterCollection)
            {
                if (!param.Value.Contains("$"))
                    continue;

                logger.WriteLine("\tparameter name = " + param.Name + ", value = " + param.Value);
                string[] valueParts = param.Value.Split('|');
                logger.WriteLine("\tpparam.Value.Split('|')");
                for (int partIndex = 0; partIndex < valueParts.Length; partIndex++)
                {
                    logger.WriteLine("\tvalueParts[" + partIndex + "] = " + valueParts[partIndex]);
                }
                string newParameterValue = "";

                for (int partIndex = 0; partIndex < valueParts.Length; partIndex++)
                {
                    string part = valueParts[partIndex];
                    string shrinkedID = "";
                    logger.WriteLine("\tSearching idDictionary with any item has value = " + valueParts[partIndex]);


                    foreach (var item in idDictionary)
                    {
                        if (part == item.Value)
                        {
                            logger.WriteLine("\tfound item with key = " + item.Key + " and value = " + part);

                            shrinkedID = "{{" + item.Key + "}}";
                            if (partIndex < valueParts.Length - 1)
                            {
                                shrinkedID += "|";
                            }
                            logger.WriteLine("\tnewValue = " + newParameterValue);
                        }
                    }

                    if (string.IsNullOrEmpty(shrinkedID))
                    {
                        logger.WriteLine("\tSearching idDictionary with any item has key = " + valueParts[partIndex]);
                        //this foreach to handel special cases where control ID which will be submitted is different than the original control ID
                        //for example 'ctl00_ContentPlaceHolder1_Details1_UC_AddUser1_upGrid' should be sumitted as 'ctl00$ContentPlaceHolder1$Details1$UC_AddUser1$upGrid' 
                        foreach (var item in idDictionary)
                        {
                            if (part == item.Key)
                            {
                                logger.WriteLine("\tfound item with key = " + item.Key + " and value = " + item.Value);

                                newParameterValue += "{{" + item.Value + "}}";
                                if (partIndex < valueParts.Length - 1)
                                {
                                    newParameterValue += "|";
                                }
                                logger.WriteLine("\tnewValue = " + newParameterValue);
                            }
                        }
                    }
                    if (string.IsNullOrEmpty(shrinkedID))
                    {
                        newParameterValue += part;
                        if (partIndex < valueParts.Length - 1)
                        {
                            newParameterValue += "|";
                        }
                    }
                    newParameterValue += shrinkedID;
                }
                if (newParameterValue == "")
                {
                    logger.WriteLine("\tnewValue == \"\"");
                    newParameterValue = param.Value;
                }
                param.Value = newParameterValue;

                logger.WriteLine("\t\t is now name = " + param.Name + ", value = " + param.Value);
            }
        }



        private bool HasNoisyValues(string paramter)
        {
            foreach (string p in parametersHasNoisyValues)
            {
                if (p == paramter)
                    return true;
            }
            return false;
        }
        Dictionary<string, string> idDictionary = new Dictionary<string, string>();

        public string ShrinkID(StreamWriter logger, string controlID)
        {
            int lastSeperatorIndex = controlID.LastIndexOf('$');
            if (lastSeperatorIndex == -1)
                return controlID;

            logger.WriteLine("\t\t\tShrinkID called");
            string lastPartOfName = controlID.Substring(lastSeperatorIndex + 1);
            int i = 1;
            string idSelector = "#" + lastPartOfName;

            while (idDictionary.ContainsKey(idSelector))
            {
                logger.WriteLine("\t\t\t\t same id selecor found in idDictionary");
                var itemValue = (IComparable)idDictionary[idSelector];

                if (itemValue.Equals(controlID))
                {
                    logger.WriteLine(string.Format("\t\t\t\tthis {0} which is mapped to {1} is already in the idDictionary", idSelector, controlID));
                    break; // We already have this exact control in Context.
                }
                else
                {
                    i++; // Another instance of the same Control. Try another number in Key.
                    idSelector = "#" + lastPartOfName + "." + i;
                    logger.WriteLine(string.Format("\t\t\t\t\tGenerating new id selecor which is {0}", idSelector, controlID));
                }
            }


            if (!idDictionary.ContainsKey(idSelector))
            {
                idDictionary.Add(idSelector, null);
                logger.WriteLine(string.Format("\t\t\tidDictionary.Add({0}, null);", idSelector));
            }

            logger.WriteLine(string.Format("\t\t\tidDictionary[{0}] = {1}", idSelector, controlID));
            idDictionary[idSelector] = controlID;
            return idSelector;
        }



        public bool ShouldBeRemoved(string formParameterName)
        {
            foreach (string noisyParam in noisyParameters)
            {
                if (formParameterName.EndsWith(noisyParam))
                    return true;
            }
            return false;
        }

    }




}
//namespace SampleRecorderPlugin
//{
//    [DisplayName("WebTestRecorderPlugin that adds title validation rule")]
//    [Description("Adds a validation rule that verifies the recorded title tag matches playback title tag value")]
//    public class RecorderPluginThatAddsTitleValidation : WebTestRecorderPlugin
//    {
//        public override void PostWebTestRecording(object sender, PostWebTestRecordingEventArgs e)
//        {
//            WebTestResultIteration recordedIteration = e.RecordedWebTestResult;
//            foreach (WebTestResultUnit resultUnit in recordedIteration.Children)
//            {
//                WebTestResultPage pageResult = resultUnit as WebTestResultPage;
//                if (pageResult != null)
//                {
//                    WebTestRequestResult finalRequestResult = pageResult.RequestResult;

//                    //if there were redirects then need to get the last redirected requests
//                    if (pageResult.RedirectedPages.Count > 0)
//                    {
//                        finalRequestResult =
//                          pageResult.RedirectedPages[pageResult.RedirectedPages.Count - 1].RequestResult;
//                    }

//                    //now get the response for the final request
//                    WebTestResponse response = finalRequestResult.Response;
//                    if (response.IsHtml)
//                    {
//                        HtmlTagInnerTextParser innerTextParser = new HtmlTagInnerTextParser(response.BodyString);
//                        HtmlTagInnerText titleInnerTextTag = null;

//                        //search for the <Title> tag
//                        foreach (HtmlTagInnerText innerTextTag in
//                                 innerTextParser.GetInnerTextForHtmlTags("title", true, false, false))
//                        {
//                            if (!string.IsNullOrEmpty(innerTextTag.Value))
//                            {
//                                titleInnerTextTag = innerTextTag;
//                                break;
//                            }
//                        }

//                        if (titleInnerTextTag == null)
//                        {
//                            continue;
//                        }

//                        //if we found a title tag then add a validation rule
//                        ValidationRuleReference validateTitleRuleReference = new ValidationRuleReference();
//                        validateTitleRuleReference.Type = typeof(ValidateHtmlTagInnerText);
//                        validateTitleRuleReference.ValidationLevel = ValidationLevel.Medium;
//                        validateTitleRuleReference.Properties.Add(new PluginOrRuleProperty("TagName", "title"));
//                        validateTitleRuleReference.Properties.Add(new PluginOrRuleProperty("ExpectedInnerText",
//                                                                                           titleInnerTextTag.Value));
//                        validateTitleRuleReference.Properties.Add(new PluginOrRuleProperty("Index", "0"));
//                        validateTitleRuleReference.Properties.Add(new PluginOrRuleProperty("RemoveInnerTags", "False"));
//                        validateTitleRuleReference.Properties.Add(new PluginOrRuleProperty("HasClosingTags", "True"));
//                        validateTitleRuleReference.Properties.Add(new PluginOrRuleProperty("IgnoreCase", "True"));
//                        validateTitleRuleReference.Properties.Add(new PluginOrRuleProperty("CollapseWhiteSpace",
//                                                                                           "False"));

//                        // Get the corresponding request in the Declarative Web test
//                        WebTestRequest requestInWebTest =
//                                e.RecordedWebTest.GetItem(pageResult.DeclarativeWebTestItemId) as WebTestRequest;
//                        if (requestInWebTest != null)
//                        {
//                            requestInWebTest.ValidationRuleReferences.Add(validateTitleRuleReference);
//                            e.RecordedWebTestModified = true;
//                        }
//                    }
//                }
//            }
//        }
//    }
//}